<?php
/**
 * 无限分类
 */
!defined('IN_FRAME') && die('404 Page');
class TreeList {
    
    /**
     * 分析出所有可能用到的数据
     */
    public $arrAll = array(); // 原始数据
    public $arrIdRelation = array(); // 按_ID作键名的多维关系
    public $arrIdRelationSimple = array(); //  按_ID作键名的多维关系的简化,用来输出树状图
    public $arrIdAll = array(); // 将原始数据转化成的_ID作键名的数组
    public $arrNameAll = array(); // 将原始数据转化成的name_en作键名的数组
    public $arrIdSon = array(); // 所有的父子关系
    public $arrIdLeaf = array(); // 叶子节点的_ID
    public $arrIdAncestor = array(); // 每个节点的祖先
    public $arrIdRoot = array(); // 根节点的_ID
    public $arrIdChildren = array(); // 每个节点下的子孙后代_ID
    public $arrIdBackPath = array(); // 每个节点回逆到根
    public $arrIdLevel = array(); // 每个层级的节点
    public $strItem = '<br />{$strSep}{$name}'; // 输出树的结构
    public $arrSeper = array('　　┊', '┈ '); // 画树的线条
    
    /**
     * 构造函数,传入原始数据
     */
    public function __construct($arrData = null) {
        $this->arrAll = $arrData;
        is_array($arrData) && $this->processData();
    }
    
    /**
     * 简单的树
     */
    public function getHtml() {
        return $this->genHtml();
    }
    
    /**
     * 用Table来画树,只用来参考
     */
    public function getTable() {
        $this->strItem = '<tr><td>{$strSep}{$name}</td><td align=\"center\">{$name}</td><td align=\"center\">{$name_en}</td></tr>';
        $strRe = '<table border="1" width="50%">';
        $strRe .= '<tr><th width="30%">结构</th><th width="20%">中文名</th><th width="10%">英文名</th></tr>';
        $strRe .= $this->genHtml();
        $strRe .= '</table>';
        return $strRe;
    }
    
    /**
     * 在下拉框中显示
     * example:
     * $objTreeList->getSelect('parent_id', 0, false, 'class="span5"', array(0, '≡ 作为一级栏目 ≡')))
     * $objTreeList->getSelect('cat_id', $catId, false, 'class="input-xlarge" onchange="this.form.submit();"', array(array(-1, '--- 所有栏目 ---'), array(0, '未指定栏目'))))
     */
    public function getSelect($strName = 'tree', $arrValue = array(), $blmMulti = false, $strExt = '', $arrFirst = null) {
        !is_array($arrValue) && $arrValue = array($arrValue);
        foreach ($this->arrIdAll as $strTemp => $arrTemp) {
            $this->arrIdAll[$strTemp]['selected'] = '';
            if (in_array($arrTemp['id'], $arrValue)) {
                $this->arrIdAll[$strTemp]['selected'] = ' selected="selected"';
            }
        }
        $this->strItem = '<option value=\"{$id}\"{$selected}>{$strSep}{$name}</option>';
        $strRe = '<select id="id_' . $strName . '" name="' . $strName . ($blmMulti ? '[]' : '') . '"';
        $strRe .= ($blmMulti ? ' multiple="multiple"' : '') . (empty($strExt) ? '' : ' ' . $strExt) . '>';
        $intTemp = Helper::getArrDeep($arrFirst);
        if (1 == $intTemp && count($arrFirst) == 2) {
            $strRe .= '<option value="' . $arrFirst[0] . '">' . $arrFirst[1] . '</option>';
        } elseif (2 == $intTemp) {
            foreach ($arrFirst as $arrTemp) {
                if (is_array($arrTemp) && count($arrTemp) == 2) {
                    $strRe .= '<option value="' . $arrTemp[0] . '"' . (in_array($arrTemp[0], $arrValue) ? ' selected="selected"' : '') . '>' . $arrTemp[1] . '</option>';
                }
            }
        }
        $strRe .= $this->getHtml() . '</select>';
        return $strRe;
    }
    
    /* ----- 以下的都是处理数据的私有函数,递归和循环之类,很复杂! ----- */
    private function helpForGetRelation($arrData) {
        $arrRe = array();
        foreach ($arrData as $strTemp => $arrTemp) {
            $arrRe[$strTemp] = $arrTemp;
            if (isset($this->arrIdRelation[$strTemp])) {
                $arrRe[$strTemp] = $this->arrIdRelation[$strTemp];
            }
            if (count($arrRe[$strTemp]) > 0) {
                $arrRe[$strTemp] = $this->helpForGetRelation($arrRe[$strTemp]);
            } else {
                array_push($this->arrIdLeaf, $strTemp);
            }
        }
        return $arrRe;
    }
    
    private function helpForGetChildren($arrData) {
        $arrRe = array_keys($arrData);
        foreach ($arrData as $arrTemp) {
            $arrRe = array_merge($arrRe, $this->helpForGetChildren($arrTemp));
        }
        return $arrRe;
    }
    
    private function helpForGetBackPath($str) {
        $arrRe = array();
        $intTemp = $this->arrIdAll[$str]['parent_id'];
        if ($intTemp > 0) {
            $intTemp = '_' . $intTemp;
            array_push($arrRe, $intTemp);
            $arrRe = array_merge($arrRe, $this->helpForGetBackPath($intTemp));
        }
        return $arrRe;
    }
    
    private function processData() {
        foreach ($this->arrAll as $arrTemp) {
            if (isset($arrTemp['name_en']) && !empty($arrTemp['name_en'])) {
                $this->arrNameAll[$arrTemp['name_en']] = $arrTemp;
            }
            $strTemp = '_' . $arrTemp['id'];
            $this->arrIdAll[$strTemp] = $arrTemp;
            if ($arrTemp['parent_id'] > 0) {
                $strTemp_ = '_' . $arrTemp['parent_id'];
                !isset($this->arrIdRelation[$strTemp_]) && $this->arrIdRelation[$strTemp_] = array();
                $this->arrIdRelation[$strTemp_][$strTemp] = array();
                !isset($this->arrIdSon[$strTemp_]) && $this->arrIdSon[$strTemp_] = array();
                array_push($this->arrIdSon[$strTemp_], $strTemp);
            } else {
                !isset($this->arrIdRelation[$strTemp]) && $this->arrIdRelation[$strTemp] = array();
                array_push($this->arrIdRoot, $strTemp);
            }
        }
        $this->arrIdRelation = $this->helpForGetRelation($this->arrIdRelation);
        $this->arrIdLeaf = array_unique($this->arrIdLeaf);
        foreach ($this->arrIdRelation as $strTemp => $arrTemp) {
            $this->arrIdChildren[$strTemp] = $this->helpForGetChildren($arrTemp);
            in_array($strTemp, $this->arrIdRoot) && $this->arrIdRelationSimple[$strTemp] = $arrTemp;
        }
        $arrTemp = array_keys($this->arrIdAll);
        foreach ($arrTemp as $strTemp) {
            $this->arrIdBackPath[$strTemp] = $this->helpForGetBackPath($strTemp);
            $this->arrIdAncestor[$strTemp] = end($this->arrIdBackPath[$strTemp]);
            $intTemp = count($this->arrIdBackPath[$strTemp]);
            isset($this->arrIdLevel['level_' . $intTemp]) || $this->arrIdLevel['level_' . $intTemp] = array();
            array_push($this->arrIdLevel['level_' . $intTemp], $strTemp);
            $this->arrIdAll[$strTemp]['__level__'] = $intTemp;
        }
    }
    
    private function genSeparator($intLen) {
        $strRe = '';
        $i = 0;
        while ($i < $intLen) {
            $strRe .= $this->arrSeper[0];
            $i ++;
        }
        !empty($strRe) && $strRe .= $this->arrSeper[1];
        return $strRe;
    }
    
    public function genHtml($arrRelation = null, $intSep = 0) {
        $strRe = '';
        null === $arrRelation && $arrRelation = $this->arrIdRelationSimple;
        foreach ($arrRelation as $strKey => $arrTemp) {
            if (count($this->arrIdAll[$strKey]) > 0) {
                $strSep = $this->genSeparator($intSep);
                extract($this->arrIdAll[$strKey]);
                eval('$strRe .= "' . $this->strItem . '";');
                count($arrTemp) > 0 && $strRe .= $this->genHtml($arrTemp, ($intSep + 1));
            }
        }
        return $strRe;
    }
}